#pragma once

#include "tinyxml2.h"
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <list>
#include <set>
#include <utility>
#include "UserType_xml.h"

using namespace tinyxml2;

//xml serialization
namespace Serialize_xml {

//以下为 class XMLSerialize, serialize和deserialize函数本体在文件最后
class XMLSerialize
{
public:
    XMLDocument doc;
    XMLElement* pos;
    const char* xmlPath;
public:
    XMLSerialize(){}
    ~XMLSerialize(){}

    void cover(const char* name, const char* xmlPath);
    void save();
    bool load(const char* name, const char* xmlPath);

    void write(bool val, const char* name);
    void write(char val, const char* name);
    void write(int val, const char* name);
    void write(float val, const char* name);
    void write(double val, const char* name);
    void write(const char* val, const char* name);
    void write(std::string val, const char* name);
    void write(UserTypeXml& obj, const char* name);

    template <typename T>
    void write(std::vector<T>& val, const char* name);
    template <typename T>
    void write(std::list<T>& val, const char* name);
    template <typename T>
    void write(std::set<T>& val, const char* name);
    template <typename K, typename V>
    void write(std::map<K,V>& val, const char* name);
    template <typename F, typename S>
    void write(std::pair<F,S>& val, const char* name);
    template <typename T, typename ...Args>
    void write_args(const T& value, const Args&... args);
    void write_args();

    bool read(bool& val);
    bool read(char& val);
    bool read(int& val);
    bool read(float& val);
    bool read(double& val);
    bool read(std::string& val);
    bool read(UserTypeXml& obj);

    template <typename T>
    bool read(std::vector<T>& val);
    template <typename T>
    bool read(std::list<T>& val);
    template <typename T>
    bool read(std::set<T>& val);
    template <typename K, typename V>
    bool read(std::map<K,V>& val);
    template <typename F, typename S>
    bool read(std::pair<F,S>& val);
    template <typename T, typename ...Args>
    bool read_args(T& value, Args&... args);
    bool read_args();

};

void XMLSerialize::cover(const char* name, const char* xmlPath)
{
    XMLElement* root;
    if(doc.LoadFile(xmlPath)!=0) 
    {
        //cout << "create" << endl;
        root = doc.NewElement("serialization");
        doc.InsertEndChild(root);
    }
    else
    {
        //cout << "追加"  << endl;
        root = doc.RootElement();
        XMLElement* userNode=root->FirstChildElement(name);
        if(userNode != NULL)    //已有则覆盖
        {
            doc.DeleteNode(userNode);
            //cout << "delete" << name << endl;
        }
    }
    pos = root;
    this->xmlPath = xmlPath;
}

bool XMLSerialize::load(const char* name, const char* xmlPath)
{
    doc.LoadFile(xmlPath);
    
	if(doc.Error())
	{
		return false;
	}

    XMLElement *root=doc.RootElement();
    pos = root->FirstChildElement(name);
    if(!pos)
    {
        return false;
    }
    
    return true;
}

void XMLSerialize::save()
{
    XMLError a = doc.SaveFile(xmlPath);
}

void XMLSerialize::write(bool val,const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    const char* s = val ? "1" : "0";
    newNode->SetAttribute("val", s);
    pos->InsertEndChild(newNode);
}

void XMLSerialize::write(char val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", val);
    pos->InsertEndChild(newNode);     
}

void XMLSerialize::write(int val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", std::to_string(val).c_str());
    pos->InsertEndChild(newNode);    
}

void XMLSerialize::write(float val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", std::to_string(val).c_str());
    pos->InsertEndChild(newNode); 
}

void XMLSerialize::write(double val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", std::to_string(val).c_str());
    pos->InsertEndChild(newNode); 
}

void XMLSerialize::write(const char* val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", val);
    pos->InsertEndChild(newNode); 
}

void XMLSerialize::write(std::string val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("val", val.c_str());
    pos->InsertEndChild(newNode); 
}

void XMLSerialize::write(UserTypeXml& obj, const char* name)
{
    obj.serialize(*this,name);
}

template <typename T>
void XMLSerialize::write(std::vector<T>& val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("size", val.size());
    pos->InsertEndChild(newNode);
    XMLElement* pre = pos;
    pos = newNode;
    for (auto it = val.begin(); it != val.end(); it++)
    {
        write(*it, "ele");
    }
    pos = pre;
}

template <typename T>
void XMLSerialize::write(std::list<T>& val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("size", val.size());
    pos->InsertEndChild(newNode);
    XMLElement* pre = pos;
    pos = newNode;
    for (auto it = val.begin(); it != val.end(); it++)
    {
        write(*it, "ele");
    }
    pos = pre;
}

template <typename T>
void XMLSerialize::write(std::set<T>& val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("size", val.size());
    pos->InsertEndChild(newNode);
    XMLElement* pre = pos;
    pos = newNode;
    for (auto it = val.begin(); it != val.end(); it++)
    {
        write(*it, "ele");
    }
    pos = pre;
}

template <typename K, typename V>
void XMLSerialize::write(std::map<K,V>& val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    newNode->SetAttribute("size", val.size());
    newNode->SetAttribute("type", "map");
    pos->InsertEndChild(newNode);
    XMLElement* pre = pos;
    pos = newNode;
    for (auto it = val.begin(); it != val.end(); it++)
    {
        write(it->first, "key");
        write(it->second, "value");
    }
    pos = pre;
}

template <typename F, typename S>
void XMLSerialize::write(std::pair<F,S>& val, const char* name)
{
    XMLElement* newNode = doc.NewElement(name);
    pos->InsertEndChild(newNode);
    XMLElement* pre = pos;
    pos = newNode;
    write(val.first, "first");
    write(val.second, "second");
    pos = pre;    
}

template <typename T, typename ...Args>
void XMLSerialize::write_args(const T& value, const Args&... args)
{
    write(value, "mem");
    write_args(args...);
}

void XMLSerialize::write_args()
{
}

bool XMLSerialize::read(bool& val)
{
    val = pos->Attribute("val")=="0" ? 0 : 1;
    return true;
}

bool XMLSerialize::read(char& val)
{
    val = (pos->Attribute("val"))[0];
    return true;
}

bool XMLSerialize::read(int& val)
{
    val = atoi(pos->Attribute("val"));
    return true;    
}

    
bool XMLSerialize::read(float& val)
{
    val = atof(pos->Attribute("val"));
    return true;      
}

bool XMLSerialize::read(double& val)
{
    val = atof(pos->Attribute("val"));
    return true;  
}

bool XMLSerialize::read(std::string& val)
{
    val = pos->Attribute("val");
    return true;  
}

bool XMLSerialize::read(UserTypeXml& val)
{
    val.deserialize(*this);
    return true;
}

template <typename T>
bool XMLSerialize::read(std::vector<T>& val)
{
    const char* n_str = pos->Attribute("size");
    if(n_str == NULL)
    {
        return false;
    }
    int n = atoi(n_str);
    val.resize(n);
    XMLElement* pre = pos;
    pos = pos->FirstChildElement("ele");
    if(pos == NULL)
    {
        return false;
    }
    auto it = val.begin();
    read(*it);
    for(it++; it != val.end(); it++)
    {
        pos = pos->NextSiblingElement();
        read(*it);
    }
    pos = pre;
    return true;
}

template <typename T>
bool XMLSerialize::read(std::list<T>& val)
{
    const char* n_str = pos->Attribute("size");
    if(n_str == NULL)
    {
        return false;
    }
    int n = atoi(n_str);
    val.resize(n);
    XMLElement* pre = pos;
    pos = pos->FirstChildElement("ele");
    if(pos == NULL)
    {
        return false;
    }
    auto it = val.begin();
    read(*it);
    for(it++; it != val.end(); it++)
    {
        pos = pos->NextSiblingElement();
        read(*it);
    }
    pos = pre;
    return true;    
}

template <typename T>
bool XMLSerialize::read(std::set<T>& val)
{
    const char* n_str = pos->Attribute("size");
    if(n_str == NULL)
    {
        return false;
    }
    int n = atoi(n_str);
    val.resize(n);
    XMLElement* pre = pos;
    pos = pos->FirstChildElement("ele");
    if(pos == NULL)
    {
        return false;
    }
    auto it = val.begin();
    read(*it);
    for(it++; it != val.end(); it++)
    {
        pos = pos->NextSiblingElement();
        read(*it);
    }
    pos = pre;
    return true;    
}

template <typename K, typename V>
bool XMLSerialize::read(std::map<K,V>& val)
{
    if(strcmp(pos->Attribute("type"),"map") != 0)
    {
        return false;
    }
    const char* n_str = pos->Attribute("size");
    if(n_str == NULL)
    {
        return false;
    }
    int n = atoi(n_str);
    val.clear();

    XMLElement* pre = pos;
    pos = pos->FirstChildElement("key");
    if(pos == NULL)
    {
        return false;
    }
    K key;
    V value;
    read(key);
    pos = pos->NextSiblingElement();
    read(value);
    val[key] = value;

    for(int i = 1; i < n; i++)
    {
        pos = pos->NextSiblingElement();
        read(key);
        pos = pos->NextSiblingElement();
        read(value);
        val[key] = value;
    }
    pos = pre;
    return true;  
}

template <typename F, typename S>
bool XMLSerialize::read(std::pair<F,S>& val)
{
    XMLElement* pre = pos;
    pos = pos->FirstChildElement("first");
    if(pos == NULL)
    {
        return false;
    }
    read(val.first);
    pos = pos->NextSiblingElement();
    read(val.second);
    pos = pre;
    return true;
}

template <typename T, typename ...Args>
bool XMLSerialize::read_args(T& value, Args&... args)
{
    read(value);
    pos = pos->NextSiblingElement();
    if (pos == NULL)
    {
        return false;
    }
    read_args(args...);
    return true;
}

bool XMLSerialize::read_args()
{
    return true;
}

// serialize object obj to an XML file with the name 
template <typename T>
void serialize_xml(T value, const char* name, const char* xmlPath)
{
    XMLSerialize xs;
    xs.cover(name, xmlPath);    //这里传入name是为了覆盖 
    xs.write(value, name);
    xs.save();
}
// reconstruct object from the content of xml file
template <typename T>
bool deserialize_xml(T& value, const char* name, const char* xmlPath)
{
    XMLSerialize xs;
    xs.load(name, xmlPath);
    xs.read(value);
    return true;
}

}